package com.icloud.gateway.filter;

import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HtmlUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.icloud.common.web.enums.StateEnum;
import com.nimbusds.jose.JWSObject;
import com.icloud.common.core.api.CommonResult;
import com.icloud.common.core.api.ResultCode;
import com.icloud.common.core.constant.AuthConstant;
import com.icloud.common.web.domain.UserDto;
import com.icloud.gateway.authorization.AccessTokenHolder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.text.ParseException;
import java.util.Objects;
import java.util.concurrent.Future;

/**
 * 将登录用户的JWT转化成用户信息的全局过滤器
 */
@Component
public class AuthGlobalFilter implements GlobalFilter, Ordered {
    @Autowired
    private AccessTokenHolder accessTokenHolder;
    //账号登录Token检测结果
    private Boolean accessTokenCheckRes = false;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //添加Golang接口访问限制（只能内网访问）
        String ip = Objects.requireNonNull(exchange.getRequest().getRemoteAddress()).getHostString();
        ip = "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : HtmlUtil.cleanHtmlTag(ip);
        //接口路由
        String path = exchange.getRequest().getURI().getPath();
        PathMatcher pathMatcher = new AntPathMatcher();

        String token = exchange.getRequest().getHeaders().getFirst(AuthConstant.JWT_TOKEN_HEADER);
        if (StrUtil.isEmpty(token)) {
            return chain.filter(exchange);
        }
        try {
            //从token中解析用户信息并设置到Header中去
            String realToken = token.replace(AuthConstant.JWT_TOKEN_PREFIX, "");
            JWSObject jwsObject = JWSObject.parse(realToken);
            String userStr = jwsObject.getPayload().toString();
            UserDto userDto = JSONUtil.toBean(userStr, UserDto.class);
            ServerHttpRequest request = exchange.getRequest().mutate().header(AuthConstant.USER_TOKEN_HEADER, userStr).build();
            // 检测token在redis中是否存在
            this.callTokenCheck(path, userDto, realToken);
            if(! accessTokenCheckRes) {
                return handleCustomException(exchange, ResultCode.UNAUTHORIZED);
            }
            //账号状态检测
            if(! StateEnum.NORMAL.getCode().equals(userDto.getState())) {
                return handleCustomException(exchange, ResultCode.ACCOUNT_DISABLED);
            }
            exchange = exchange.mutate().request(request).build();
            //仅供内网访问的接口
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return 0;
    }

    /**
     * 获得账号token的检测结果
     */
    private void callTokenCheck(String path, UserDto userDto, String realToken) {
        Future<Boolean> booleanFuture = accessTokenHolder.checkAccountAccessToken(path, userDto);
        try {
            accessTokenCheckRes = booleanFuture.get();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 返回自定义异常信息
     */
    private Mono<Void> handleCustomException(ServerWebExchange exchange, ResultCode resultCode) {
        //构造JSON返回结果
        CommonResult<Object> resultObj = CommonResult.builder().code(resultCode.getCode()).message(resultCode.getMessage()).data(null).build();
        byte[] resultByte = JSON.toJSONString(resultObj).getBytes();
        //返回异常
        return exchange.getResponse().writeWith(Flux.just(
                exchange.getResponse().bufferFactory().wrap(resultByte)
        ));
    }
}
